﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using Noris.Schedule.Support;

namespace Noris.Tools.FrxEditor.Components
{
    #region class DForm : základní třída všech formulářů systému
    /// <summary>
    /// DForm : základní třída všech formulářů systému.
    /// Deklaruje svůj ToolTip a objekt ToolTipSupport pro podporu řízení pozice tooltipu.
    /// </summary>
    public class DForm : Form
    {
        #region Konstrukce
        /// <summary>
        /// DForm : základní třída všech formulářů systému. Konstruktor.
        /// </summary>
        public DForm()
        {
			this.PersistBoundary = true;
            InitializeComponent();
            this._ToolTipSupport = new ToolTipSupport();
            this.ToolTip.Popup += new PopupEventHandler(ToolTip_Popup);
            this._ToolTipSupport.InitToolTip(this.ToolTip);
			this.Load += new EventHandler(DForm_Load);
			this.FormClosed += new FormClosedEventHandler(DForm_FormClosed);
        }
		void DForm_Load(object sender, EventArgs e)
		{
			if (this.PersistBoundary)
				Steward.WindowLoadBounds(this);
		}
		void DForm_FormClosed(object sender, FormClosedEventArgs e)
		{
			if (this.PersistBoundary)
				Steward.WindowSaveBounds(this);
		}
        void ToolTip_Popup(object sender, PopupEventArgs e)
        {
            this._ToolTipSupport.ToolTip_Popup(sender, e);
        }
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DForm));
            this.ToolTip = new System.Windows.Forms.ToolTip(this.components);
            this.SuspendLayout();
            // 
            // ToolTip
            // 
            this.ToolTip.Active = false;
            // 
            // DForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Name = "DForm";
            this.ShowInTaskbar = false;
            this.Text = "Formulář";
            this.ResumeLayout(false);
        }
        /// <summary>
        /// Tooltip na formuláři
        /// </summary>
        public System.Windows.Forms.ToolTip ToolTip;
        #endregion
		#region Property
		/// <summary>
		/// PersistBoundary: Ukládat Bounds okna (rozměry a pozice) do paměti, takže příště se okno otevře tam, kde se posledně zavřelo.
		/// Default = true.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Category("DForm")]
		[DefaultValue(true)]
		public bool PersistBoundary { get; set; }
		#endregion
        #region Tooltip
        /// <summary>
        /// Podpora pro tooltip
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ToolTipSupport ToolTipSupport { get { return this._ToolTipSupport; } }
        private ToolTipSupport _ToolTipSupport;
        #endregion
    }
    #endregion
    #region class ToolTipSupport : podpůrná třída pro zobrazování systémového tooltipu.
    /// <summary>
    /// ToolTipSupport : podpůrná třída pro zobrazování systémového tooltipu.
    /// Instancuje se automaticky na Form (jedna instance) a řeší kompletní podporu pro živý tooltip 
    /// (detekci změny pozice myši, pozicování tooltipu na pozici myši, zhasínání při opouštění controlu).
    /// </summary>
    public class ToolTipSupport
    {
        #region Konstrukce
        /// <summary>
        /// ToolTipSupport : podpůrná třída pro zobrazování systémového tooltipu. Konstruktor.
        /// </summary>
        public ToolTipSupport()
        {
            this._DataDict = new Dictionary<Control, ToolTipSupportData>();
            this.SetDefaults();
        }
        /// <summary>
        /// Nastaví default hodnoty
        /// </summary>
        protected void SetDefaults()
        {
            this.DefaultIcon = ToolTipIcon.Info;
            this.DefaultTitle = null;
            this.DefaultIsBaloon = false;
            this.IgnoreMoveBelow = 6;
            this.ToolTipOffset = new Point(8, 12);
            this._ImplicitTitle = null;
        }
        private Dictionary<Control, ToolTipSupportData> _DataDict;
        /// <summary>
        /// Příznak, zda máme zachycenu referenci na tooltip (v _ToolTip, v _ToolTipRef)
        /// </summary>
        private bool _HasTooltip
        {
            get
            {
                return (_ToolTipRef != null && _ToolTipRef.IsAlive && _ToolTipRef.Target is ToolTip);
            }
        }
        private WeakReference _ToolTipRef;
        #endregion
        #region Public property
        /// <summary>
        /// Implicitní ikona
        /// </summary>
        public ToolTipIcon DefaultIcon { get; set; }
        /// <summary>
        /// Implicitní titulek
        /// </summary>
        public string DefaultTitle { get; set; }
        /// <summary>
        /// Implicitní stav IsBaloon (vřele doporučuji false, přinejmenším pro pohyblivé tooltipy)
        /// </summary>
        public bool DefaultIsBaloon { get; set; }
        /// <summary>
        /// Weak referencovaný ToolTip.
        /// Pouze, pokud prošel naší inicializací, viz this._HasTooltip
        /// </summary>
        public ToolTip ToolTip
        {
            get
            {
                if (_HasTooltip)
                    return _ToolTipRef.Target as ToolTip;
                return null;
            }
            set
            {
                if (value == null)
                    _ToolTipRef = null;
                else
                    _ToolTipRef = new WeakReference(value);
            }
        }
        /// <summary>
        /// Ignorovat pohyb myši menší než tato vzdálenost.
        /// Implicitně 6 pixelů.
        /// </summary>
        public int IgnoreMoveBelow { get; set; }
        /// <summary>
        /// Posun bodu Tooltipu od souřadnice myši. Měl by být kladný, default = {8; 12}
        /// </summary>
        public Point ToolTipOffset { get; set; }
        #endregion
        #region Public instanční metody pro podporu definice vlastností tooltipu na instanci třídy Control
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu a titulek.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        /// <param name="toolTipTitle"></param>
        public void SetToolTip(Control control, ToolTipIcon? toolTipIcon, string toolTipTitle)
        {
            this.SetToolTip(control, toolTipIcon, toolTipTitle, (bool?)null);
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu a titulek.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        /// <param name="toolTipTitle"></param>
        /// <param name="toolTipIsBaloon">Chcete balonový tooltip? 
        /// Očekávejte potíže, pokud budete kombinovat balonový statický tooltip s dynamickým (mouse tracking) tooltipem. 
        /// Stížnosti (ne)řeší Microsoft.</param>
        public void SetToolTip(Control control, ToolTipIcon? toolTipIcon, string toolTipTitle, bool? toolTipIsBaloon)
        {
            ToolTipSupportData data = _GetData(control);
            data.Icon = toolTipIcon;
            data.Title = toolTipTitle;
            data.IsBaloon = toolTipIsBaloon;
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        public void SetToolTipIcon(Control control, ToolTipIcon toolTipIcon)
        {
            ToolTipSupportData data = _GetData(control);
            data.Icon = toolTipIcon;
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip daný titulek.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipTitle"></param>
        public void SetToolTipTitle(Control control, string toolTipTitle)
        {
            ToolTipSupportData data = _GetData(control);
            data.Title = toolTipTitle;
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip daný baloon.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="isBaloon"></param>
        public void SetToolTipTitleBaloon(Control control, bool isBaloon)
        {
            ToolTipSupportData data = _GetData(control);
            data.IsBaloon = isBaloon;
        }
        #endregion
        #region Public instanční metody - podpora pro zobrazení a skrývání tooltipu
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Tooltip není nutno předávat, pokud prošel zdejší inicializací (odchytlila se weakreference na něj).
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="text">Text do tooltipu</param>
        public void ShowToolTipAtCurrentPosition(Control control, string text)
        {
            if (_HasTooltip)
                this._ShowToolTipAtCurrentPosition(control, this.ToolTip, text, GetDurationFromTextLength(text));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Tooltip není nutno předávat, pokud prošel zdejší inicializací (odchytlila se weakreference na něj).
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="durationByLength">Nastavit Duration podle délky textu</param>
        public void ShowToolTipAtCurrentPosition(Control control, string text, bool durationByLength)
        {
            if (_HasTooltip)
                this._ShowToolTipAtCurrentPosition(control, this.ToolTip, text, (durationByLength ? GetDurationFromTextLength(text) : this.ToolTip.AutoPopDelay));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        public void ShowToolTipAtCurrentPosition(Control control, ToolTip toolTip, string text)
        {
            this._ShowToolTipAtCurrentPosition(control, toolTip, text, GetDurationFromTextLength(text));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="durationByLength">Nastavit Duration podle délky textu</param>
        public void ShowToolTipAtCurrentPosition(Control control, ToolTip toolTip, string text, bool durationByLength)
        {
            this._ShowToolTipAtCurrentPosition(control, toolTip, text, (durationByLength ? GetDurationFromTextLength(text) : toolTip.AutoPopDelay));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="duration">Doba, po kterou bude tooltip svítit</param>
        public void ShowToolTipAtCurrentPosition(Control control, ToolTip toolTip, string text, int duration)
        {
            this._ShowToolTipAtCurrentPosition(control, toolTip, text, duration);
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Tooltip není nutno předávat, pokud prošel zdejší inicializací (odchytlila se weakreference na něj).
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="text">Text do tooltipu</param>
        public void ShowToolTipAtPoint(Control control, Point point, string text)
        {
            if (_HasTooltip)
                this._ShowToolTipAtPoint(control, point, this.ToolTip, text, GetDurationFromTextLength(text));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Tooltip není nutno předávat, pokud prošel zdejší inicializací (odchytlila se weakreference na něj).
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="durationByLength">Nastavit Duration podle délky textu</param>
        public void ShowToolTipAtPoint(Control control, Point point, string text, bool durationByLength)
        {
            if (_HasTooltip)
                this._ShowToolTipAtPoint(control, point, this.ToolTip, text, (durationByLength ? GetDurationFromTextLength(text) : this.ToolTip.AutoPopDelay));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        public void ShowToolTipAtPoint(Control control, Point point, ToolTip toolTip, string text)
        {
            this._ShowToolTipAtPoint(control, point, toolTip, text, GetDurationFromTextLength(text));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="durationByLength">Nastavit Duration podle délky textu</param>
        public void ShowToolTipAtPoint(Control control, Point point, ToolTip toolTip, string text, bool durationByLength)
        {
            this._ShowToolTipAtPoint(control, point, toolTip, text, (durationByLength ? GetDurationFromTextLength(text) : toolTip.AutoPopDelay));
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="duration">Doba, po kterou bude tooltip svítit</param>
        public void ShowToolTipAtPoint(Control control, Point point, ToolTip toolTip, string text, int duration)
        {
            this._ShowToolTipAtPoint(control, point, toolTip, text, duration);
        }
        /// <summary>
        /// Podpora pro externí metodu, která chce rozsvítit tooltip pro daný control na dané pozici myši.
        /// Tooltip sám nebudeme rozsvěcovat, zde pouze určíme zda se rozsvítit má a kde.
        /// Pokud ano, bude vyvolána akce (parametr action), již se předá bod (Point), na kterém se má tooltip rozsvítit.
        /// Akce (typicky anonymní metoda) je tedy spuštěna jen tehdy, když se má tooltip rozsvítit, a jako parametr dostane konkrétní bod.
        /// Akce může teprve poté určit text tooltipu, nastavit titulek, zadat trvání tooltipu atd, a zobrazit si jej.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="action">
        /// Akce, která se zavolá v případě, kdy se má rozsvítit tooltip.
        /// Pro jistotu: anonymní metoda se píše: "delegate(Point point) { _ToolTip.Show("Text", control, point); }", kde "control" je to, co se předává i sem jakožto control.
        /// Kdo to splete (sem pošle jeden control, a tooltip zobrazí pro dočista jiný control), nesmí se divit, že se mu tooltip zobrazí někde kdesi mimo mísu!!!
        /// </param>
        public void PrepareToolTipForCurrentPosition(Control control, Action<Point> action)
        {
            Point point;
            if (action != null && _DetectMousePositionChanged(control, out point))
                action(point);
        }
        /// <summary>
        /// Vrátí přiměřenou dobu svícení tooltipu pro daný text.
        /// Pracuje se s tím, že uživatel dokáže přečíst cca 30 znaků za sekundu.
        /// Minimum nechť je 1,6 sekundy.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static int GetDurationFromTextLength(string text)
        {
            float duration = 1.9f;
            if (text != null && text.Length > 48)
                duration = 0.6f + (float)text.Length / 28f;
            return (int)(duration * 1000f);
        }
        /// <summary>
        /// Zajistí zhasnutí implicitního tooltipu pro daný control.
        /// </summary>
        /// <param name="control"></param>
        public void HideToolTipForControl(Control control)
        {
            if (_HasTooltip)
                this._HideToolTipForControl(control, this.ToolTip);
        }
        /// <summary>
        /// Zajistí zhasnutí tooltipu pro daný control.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTip"></param>
        public void HideToolTipForControl(Control control, ToolTip toolTip)
        {
            this._HideToolTipForControl(control, toolTip);
        }
        #endregion
        #region Podpora pro inicializaci tooltipu
        /// <summary>
        /// Inicializuje tooltip.
        /// Současně si na něj uchová nehasnoucí vzpomínku (v podobě WeakReference).
        /// Pak lze využívat jiné metody bez explicitního uvedení tooltipu.
        /// </summary>
        /// <param name="toolTip"></param>
        public void InitToolTip(ToolTip toolTip)
        {
            _InitToolTip(toolTip, false, ToolTipIcon.None, "Informace");
        }
        /// <summary>
        /// Inicializuje tooltip.
        /// Současně si na něj uchová nehasnoucí vzpomínku (v podobě WeakReference).
        /// Pak lze využívat jiné metody bez explicitního uvedení tooltipu.
        /// </summary>
        /// <param name="toolTip">Objekt tooltipu</param>
        /// <param name="isBaloon">Příznak, že chceme balonový tooltip. Pro tooltip, který reaguje na pohyb myši se VŘELE NEDOPORUČUJE.</param>
        public void InitToolTip(ToolTip toolTip, bool isBaloon)
        {
            _InitToolTip(toolTip, isBaloon, ToolTipIcon.None, "Informace");
        }
        /// <summary>
        /// Inicializuje tooltip.
        /// Současně si na něj uchová nehasnoucí vzpomínku (v podobě WeakReference).
        /// Pak lze využívat jiné metody bez explicitního uvedení tooltipu.
        /// </summary>
        /// <param name="toolTip">Objekt tooltipu</param>
        /// <param name="icon">Ikona tooltipu</param>
        /// <param name="title">Titulní (tučný) řádek tooltipu</param>
        public void InitToolTip(ToolTip toolTip, ToolTipIcon icon, string title)
        {
            _InitToolTip(toolTip, false, icon, title);
        }
        /// <summary>
        /// Inicializuje tooltip.
        /// Současně si na něj uchová nehasnoucí vzpomínku (v podobě WeakReference).
        /// Pak lze využívat jiné metody bez explicitního uvedení tooltipu.
        /// </summary>
        /// <param name="toolTip">Objekt tooltipu</param>
        /// <param name="isBaloon">Příznak, že chceme balonový tooltip. Pro tooltip, který reaguje na pohyb myši se VŘELE NEDOPORUČUJE.</param>
        /// <param name="icon">Ikona tooltipu</param>
        /// <param name="title">Titulní (tučný) řádek tooltipu</param>
        public void InitToolTip(ToolTip toolTip, bool isBaloon, ToolTipIcon icon, string title)
        {
            _InitToolTip(toolTip, isBaloon, icon, title);
        }
        /// <summary>
        /// Inicializace
        /// </summary>
        /// <param name="toolTip">Objekt tooltipu</param>
        /// <param name="isBaloon">Příznak, že chceme balonový tooltip. Pro tooltip, který reaguje na pohyb myši se VŘELE NEDOPORUČUJE.</param>
        /// <param name="icon">Ikona tooltipu</param>
        /// <param name="title">Titulní (tučný) řádek tooltipu</param>
        private void _InitToolTip(ToolTip toolTip, bool isBaloon, ToolTipIcon icon, string title)
        {
            toolTip.Active = true;                     // true !   Jinak se přes Show nerozsvítí
            toolTip.AutomaticDelay = 350;
            toolTip.AutoPopDelay = (isBaloon ? 3500 : 2400);
            toolTip.BackColor = SystemColors.Info;
            toolTip.InitialDelay = (isBaloon ? 350 : 100);
            toolTip.IsBalloon = isBaloon;              // IsBalloon = true : vůbec se nehodí k použití spolu s řízením pozice (Show at Point). Bliká, a to včetně pozadí!!!
            toolTip.ReshowDelay = 650;
            toolTip.ShowAlways = isBaloon;
            toolTip.ToolTipIcon = icon;
            toolTip.ToolTipTitle = title;
            toolTip.UseAnimation = isBaloon;
            toolTip.UseFading = isBaloon;

            // Opíšeme si ikonu a titulek:
            this.DefaultIcon = icon;
            this.DefaultTitle = title;
            this.DefaultIsBaloon = isBaloon;

            // Uchováme si WeakReferenci:
            this.ToolTip = toolTip;
        }
        #endregion
        #region Eventy ToolTipu
        /// <summary>
        /// Handler pro událost, kdy se zobrazuje tooltip.
        /// Událost vyvolává ToolTip, reaguje na ni DForm, a předává ji nám (protože my jsme v DForm hostováni).
        /// Zde můžeme ještě nastavit vlastnosti tooltipu podle konkrétního objektu, pro který se tooltip rozsvěcuje.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void ToolTip_Popup(object sender, PopupEventArgs e)
        {
            ToolTip toolTip = sender as ToolTip;
            ToolTipSupportData data = _GetData(e.AssociatedControl);

            // Ikona:
            if (data.Icon.HasValue)
                toolTip.ToolTipIcon = data.Icon.Value;
            else
                toolTip.ToolTipIcon = this.DefaultIcon;

            // Titulek:
            if (data.Title != null)
                toolTip.ToolTipTitle = data.Title;
            else
                toolTip.ToolTipTitle = this.DefaultTitle;

            // Baloon:
            if (data.IsBaloon.HasValue)
                toolTip.IsBalloon = data.IsBaloon.Value;
            else
                toolTip.IsBalloon = this.DefaultIsBaloon;
        }
        #endregion
        #region Static services
        /// <summary>
        /// Vrací pozici, na které v rámci controlu začíná jeho vnitřní užitná plocha.
        /// Control jako celek zabírá i border + title, ale vnitřní plocha se počítá bez těchto vnějších prvků.
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static Point GetControlClientOffset(Control control)
        {
            Size sizeIn = control.ClientSize;
            Size sizeOut = control.Size;
            int borderL = (sizeOut.Width - sizeIn.Width) / 2;
            int borderT = sizeOut.Height - sizeIn.Height - borderL;
            return new Point(borderL, borderT);
        }
        /// <summary>
        /// Vrátí tu vyšší vzdálenost mezi dvěma body ve směru X nebo Y, vrací kladné číslo nebo (0 když jsou body identické)
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public static int GetDistanceXY(Point p1, Point p2)
        {
            int dx = Math.Abs(p1.X - p2.X);
            int dy = Math.Abs(p1.Y - p2.Y);
            return Math.Max(dx, dy);
        }
        #endregion
        #region Private metody pro řízení tooltipu
        /// <summary>
        /// Metoda určí, zda aktuální pozice myši má způsobit rozsvícení tooltipu (řeší pohyb / nepohyb),
        /// a kde se má tooltip rozsvítit (řeší konverzi souřadnic myši na souřadnice v prostoru Controlu).
        /// Výstupem je souřadnice bodu, na kterém se má rozsvítit tooltip pro daný control.
        /// Je odvozen od souřadnice myši relativné k počátku controlu s přídavkem ToolTipOffset
        /// </summary>
        /// <param name="control"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        private bool _DetectMousePositionChanged(Control control, out Point point)
        {
            Point mousePosition = Control.MousePosition;
            ToolTipSupportData data = _GetData(control);
            point = Point.Empty;

            // Pokud byl nedávno tooltip zobrazen, a nebyl zhasnut (viz metoda this._OnMouseLeave()), a pokud se myš od poslední známé pozice moc nevzdálila, pak nebudeme nic řešit:
            if (data.ToolTipIsShowed)
            {
                int distance = GetDistanceXY(mousePosition, data.LastMousePosition);
                if (distance == 0 || distance < this.IgnoreMoveBelow) return false;
            }

            data.LastMousePosition = mousePosition;

            // Určím relativní souřadnici myši v prostoru daného Controlu:
            if (control is Form)
                point = new Point(
                    mousePosition.X - control.Location.X + this.ToolTipOffset.X,
                    mousePosition.Y - control.Location.Y + this.ToolTipOffset.Y);
            else
            {
                Point clientInScreen = control.PointToScreen(new Point(0, 0));       // Absolutní souřadnice klientské části controlu (nikoli ale control jako takový)
                Point clientOffset = GetControlClientOffset(control);                // Posun klientské části controlu proti jeho vlastnímu počátku controlu
                point = new Point(
                    mousePosition.X - (clientInScreen.X - clientOffset.X) + this.ToolTipOffset.X,
                    mousePosition.Y - (clientInScreen.Y - clientOffset.Y) + this.ToolTipOffset.Y);
            }

            data.ToolTipIsShowed = true;
            return true;
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Control, o nějž se jedná</param>
        /// <param name="toolTip">Tooltip, který se má rozsvítit</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="duration">Doba, po kterou bude tooltip svítit</param>
        private void _ShowToolTipAtCurrentPosition(Control control, ToolTip toolTip, string text, int duration)
        {
            Point point;
            if (_DetectMousePositionChanged(control, out point))
                this._ShowToolTipAtPoint(control, point, toolTip, text, duration);
        }
        /// <summary>
        /// Rozsvítí tooltip na dané pozici.
        /// Řeší explicitní / implicitní titulek a ikonu.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTip"></param>
        /// <param name="point"></param>
        /// <param name="text"></param>
        /// <param name="duration"></param>
        private void _ShowToolTipAtPoint(Control control, Point point, ToolTip toolTip, string text, int duration)
        {
            this._SetIconAndTitle(control, toolTip);
            toolTip.Show(text, control, point, duration);
        }
        /// <summary>
        /// Řeší explicitní / implicitní ikonu a titulek tooltipu.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTip"></param>
        private void _SetIconAndTitle(Control control, ToolTip toolTip)
        {
            ToolTipIcon? exToolTipIcon = null;
            string exToolTipTitle = null;
            if (control is IToolTipExtendedData)
            {
                IToolTipExtendedData extended = control as IToolTipExtendedData;
                exToolTipIcon = extended.ToolTipIcon;
                exToolTipTitle = extended.ToolTipTitle;
            }

            // Ikona:
            if (exToolTipIcon != null)
                toolTip.ToolTipIcon = exToolTipIcon.Value;
            else
                toolTip.ToolTipIcon = this.DefaultIcon;

            // Titulek:
            if (exToolTipTitle != null)
                toolTip.ToolTipTitle = exToolTipTitle;
            else
                toolTip.ToolTipTitle = this._ImplicitTitle;
        }
        private string _ImplicitTitle;
        /// <summary>
        /// Zhasne tooltip a zapíše si to za uši (tj. do paměti)
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTip"></param>
        private void _HideToolTipForControl(Control control, ToolTip toolTip)
        {
            if (this._DataDict.ContainsKey(control))
                this._DataDict[control].ToolTipIsShowed = false;
            toolTip.Hide(control);
        }
        /// <summary>
        /// Najde nebo založí data pro control a vrátí je.
        /// Vždy vrací data, kromě případu kdy control je null.
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        private ToolTipSupportData _GetData(Control control)
        {
            ToolTipSupportData data;
            if (!this._DataDict.TryGetValue(control, out data))
            {
                data = new ToolTipSupportData(control);
                this._DataDict.Add(control, data);
            }
            return data;
        }
        #endregion
        #region subclass ToolTipSupportData
        private class ToolTipSupportData
        {
            internal ToolTipSupportData(Control control)
            {
                this.Control = control;
            }
            internal Control Control;
            internal ToolTipIcon? Icon;
            internal string Title;
            internal bool? IsBaloon;
            internal Point LastMousePosition;
            internal bool ToolTipIsShowed;
        }
        #endregion
    }
    #endregion
    #region class ControlExtension : extenze třídy Control, která dovoluje každému controlu řídit zobrazování "svého" tooltipu
    /// <summary>
    /// ControlExtension : extenze třídy Control, která dovoluje každému controlu řídit zobrazování "svého" tooltipu
    /// </summary>
    public static class ControlExtension
    {
        #region Extense pro podporu definice vlastností tooltipu na instanci třídy Control
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu a titulek.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        /// <param name="toolTipTitle"></param>
        public static void SetToolTip(this Control control, ToolTipIcon? toolTipIcon, string toolTipTitle)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.SetToolTip(control, toolTipIcon, toolTipTitle);
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu a titulek.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        /// <param name="toolTipTitle"></param>
        /// <param name="toolTipIsBaloon">Chcete balonový tooltip? 
        /// Očekávejte potíže, pokud budete kombinovat balonový statický tooltip s dynamickým (mouse tracking) tooltipem. 
        /// Stížnosti (ne)řeší Microsoft.</param>
        public static void SetToolTip(this Control control, ToolTipIcon? toolTipIcon, string toolTipTitle, bool? toolTipIsBaloon)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.SetToolTip(control, toolTipIcon, toolTipTitle, toolTipIsBaloon);
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu, titulek a text
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        /// <param name="toolTipTitle"></param>
        /// <param name="toolTipText"></param>
        public static void SetToolTip(this Control control, ToolTipIcon toolTipIcon, string toolTipTitle, string toolTipText)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
            {
                dForm.ToolTipSupport.SetToolTip(control, toolTipIcon, toolTipTitle);
                dForm.ToolTip.SetToolTip(control, toolTipText);
            }
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu, titulek a text
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        /// <param name="toolTipTitle"></param>
        /// <param name="toolTipText"></param>
        /// <param name="toolTipIsBaloon">Chcete balonový tooltip? 
        /// Očekávejte potíže, pokud budete kombinovat balonový statický tooltip s dynamickým (mouse tracking) tooltipem. 
        /// Stížnosti (ne)řeší Microsoft.</param>
        public static void SetToolTip(this Control control, ToolTipIcon toolTipIcon, string toolTipTitle, string toolTipText, bool? toolTipIsBaloon)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
            {
                dForm.ToolTipSupport.SetToolTip(control, toolTipIcon, toolTipTitle, toolTipIsBaloon);
                dForm.ToolTip.SetToolTip(control, toolTipText);
            }
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip danou ikonu.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipIcon"></param>
        public static void SetToolTipIcon(this Control control, ToolTipIcon toolTipIcon)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.SetToolTipIcon(control, toolTipIcon);
        }
        /// <summary>
        /// Zapamatuje si, že pro daný control má mít tooltip daný titulek.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipTitle"></param>
        public static void SetToolTipTitle(this Control control, string toolTipTitle)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.SetToolTipTitle(control, toolTipTitle);
        }
        /// <summary>
        /// Zadá / změní text do tooltipu pro tento control
        /// </summary>
        /// <param name="control"></param>
        /// <param name="toolTipText"></param>
        public static void SetToolTipText(this Control control, string toolTipText)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTip.SetToolTip(control, toolTipText);
        }
        #endregion
        #region Extense pro podporu přímého zobrazování ToolTipu přímo na instanci třídy Control (extension instanční metody) ShowToolTipAtCurrentPosition, PrepareToolTipForCurrentPosition, HideToolTipForControl
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="text">Text do tooltipu</param>
        public static void ShowToolTipAtCurrentPosition(this Control control, string text)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.ShowToolTipAtCurrentPosition(control, dForm.ToolTip, text);
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="durationByLength">Nastavit Duration podle délky textu</param>
        public static void ShowToolTipAtCurrentPosition(this Control control, string text, bool durationByLength)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.ShowToolTipAtCurrentPosition(control, dForm.ToolTip, text, durationByLength);
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na aktuální pozici myši, s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="duration">Doba, po kterou bude tooltip svítit</param>
        public static void ShowToolTipAtCurrentPosition(this Control control, string text, int duration)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.ShowToolTipAtCurrentPosition(control, dForm.ToolTip, text, duration);
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="text">Text do tooltipu</param>
        public static void ShowToolTipAtPoint(this Control control, Point point, string text)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.ShowToolTipAtPoint(control, point, dForm.ToolTip, text);
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="durationByLength">Nastavit Duration podle délky textu</param>
        public static void ShowToolTipAtPoint(this Control control, Point point, string text, bool durationByLength)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.ShowToolTipAtPoint(control, point, dForm.ToolTip, text, durationByLength);
        }
        /// <summary>
        /// Zajistí rozsvícení ToolTipu pro daný control, na dané pozici (pozice v rámci Controlu!), s daným textem.
        /// Pokud je myš v malé vzdálenosti (viz this.IgnoreMoveBelow) od bodu, kde se posledně rozsvítila, nic se dělat nebude.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="point">Bod pro rozsvícení tooltipu</param>
        /// <param name="text">Text do tooltipu</param>
        /// <param name="duration">Doba, po kterou bude tooltip svítit</param>
        public static void ShowToolTipAtPoint(this Control control, Point point, string text, int duration)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.ShowToolTipAtPoint(control, point, dForm.ToolTip, text, duration);
        }
        /// <summary>
        /// Podpora pro externí metodu, která chce rozsvítit tooltip pro daný control na dané pozici myši.
        /// Tooltip sám nebudeme rozsvěcovat, zde pouze určíme zda se rozsvítit má a kde.
        /// Pokud ano, bude vyvolána akce (parametr action), již se předá bod (Point), na kterém se má tooltip rozsvítit.
        /// Akce (typicky anonymní metoda) je tedy spuštěna jen tehdy, když se má tooltip rozsvítit, a jako parametr dostane konkrétní bod.
        /// Akce může teprve poté určit text tooltipu, nastavit titulek, zadat trvání tooltipu atd, a zobrazit si jej.
        /// </summary>
        /// <param name="control">Daný control</param>
        /// <param name="action">
        /// Akce, která se zavolá v případě, kdy se má rozsvítit tooltip.
        /// Pro jistotu: anonymní metoda se píše: "delegate(Point point) { _ToolTip.Show("Text", control, point); }", kde "control" je to, co se předává i sem jakožto control.
        /// Kdo to splete (sem pošle jeden control, a tooltip zobrazí pro dočista jiný control), nesmí se divit, že se mu tooltip zobrazí někde kdesi mimo mísu!!!
        /// </param>
        public static void PrepareToolTipForCurrentPosition(this Control control, Action<Point> action)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.PrepareToolTipForCurrentPosition(control, action);
        }
        /// <summary>
        /// Zajistí zhasnutí implicitního tooltipu pro daný control.
        /// </summary>
        /// <param name="control">Daný control</param>
        public static void HideToolTipForControl(this Control control)
        {
            DForm dForm = control.GetDForm();
            if (dForm != null)
                dForm.ToolTipSupport.HideToolTipForControl(control, dForm.ToolTip);
        }
        #endregion
        #region Extense pro podporu vyhledání formuláře jako objektu třídy DForm
        /// <summary>
        /// Vrátí formulář, na němž je umístěn control, přetypovaný na DForm (základní třída formulářů).
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static DForm GetDForm(this Control control)
        {
            DForm dForm = control.FindForm() as DForm;
            return dForm;
        }
        #endregion
    }
    #endregion
    #region interface IToolTipExtendedData : předepisuje vlastnosti objektu, které mohou ovlivnit vzhled tooltipu pro tento objekt
    /// <summary>
    /// IToolTipExtendedData : předepisuje vlastnosti objektu, které mohou ovlivnit vzhled tooltipu pro tento objekt
    /// </summary>
    public interface IToolTipExtendedData
    {
        /// <summary>
        /// Ikona pro ToolTip pro tento control.
        /// Pokud je null, ikona se nemění.
        /// </summary>
        ToolTipIcon? ToolTipIcon { get; }
        /// <summary>
        /// Titulek pro ToolTip pro tento control.
        /// Pokud je null, titulek se nemění.
        /// Pokud je String.Empty, je ToolTip pro tento control zobrazen bez titulku.
        /// </summary>
        string ToolTipTitle { get; }
    }
    #endregion
}
